﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace HobSoft.SoccerBet2014.Domain.Implementation.Local
{
    using Core;
    using IRepositories;
    using Models;
    using Models.KeyHelper;
    using SoccerBet2014.Common.Queryable;

    public class GameRepository : ReadWriteBinaryRepository<Game, string>, IGameRepository
    {
        protected override Func<Game, string> FindKey
        {
            get { return g => g.Key; }
        }

        protected virtual void TryRemoveRelatedEntities(Game delGame)
        {
            var marketModels = BinaryContext.Current.Get<Market, string>();
            var oddsModels = BinaryContext.Current.Get<Odds, string>();
            var delMarketKeys = marketModels.Where(m => m.Value.GameID == delGame.GameID && m.Value.BmID == delGame.BmID).Select(m => m.Key).ToArray();
            var delOddsKeys = oddsModels.Where(m => m.Value.GameID == delGame.GameID && m.Value.BmID == delGame.BmID).Select(m => m.Key).ToArray();
            foreach (var mKey in delMarketKeys)
            {
                Market delMk;
                marketModels.TryRemove(mKey, out delMk);
            }
            foreach (var oKey in delOddsKeys)
            {
                Odds delOd;
                oddsModels.TryRemove(oKey, out delOd);
            }
        }

        public override bool Delete(Game model)
        {
            if (model == null) return true;
            Game delGame;
            BinModels.TryRemove(model.Key, out delGame);

            TryRemoveRelatedEntities(model);
            return true;
        }

        public override bool Delete(params object[] id)
        {
            if (id == null || id.Length < 1) throw new ArgumentNullException("need id");
            var key = id[0].ToString();
            Game delGame;
            if (BinModels.TryRemove(key, out delGame))
                TryRemoveRelatedEntities(delGame);
            return base.Delete(id);
        }

        public override int DeleteMany(Expression<Func<Game, bool>> predicate)
        {
            if (predicate == null)
                throw new ArgumentNullException("predicate", "need predicate");
            var lambda = predicate.Compile();
            var query = BinModels.Where(g => lambda(g.Value)).Select(g => g.Key);
            foreach (var gKey in query)
            {
                Game delGame;
                if(BinModels.TryRemove(gKey,out delGame))
                    TryRemoveRelatedEntities(delGame);
            }
            return base.DeleteMany(predicate);
        }

        public bool BackupPreGame(byte bookerId, string id)
        {
            //throw new NotSupportedException("BinaryRepository no Support.");
            return true;
        }

        public int Finish(byte bookerId, params string[] ids)
        {
            if (ids == null)
                throw new ArgumentNullException("ids", "need ids");
            if (!ids.Any()) return 0;

            var query = from mod in BinModels
                        join id in ids on mod.Key equals KeyGenerateHelper.Create<Game>(bookerId, id)
                        select mod.Value;
            foreach (var game in query)
            {
                game.State = Game.GameState.Finish;
                game.LastUpdateTime = DateTime.Now;
            }
            return query.Count();
        }

        public int UpdateRelatedEntities(IList<Game> games, IList<Market> marketRange, IList<Odds> oddsRange)
        {
            if (games == null)
                throw new ArgumentNullException("games", "need games");
            if (!games.Any()) return 0;

            AddOrUpdate(games);
            var marketModels = BinaryContext.Current.Get<Market, string>();
            var oddsModels = BinaryContext.Current.Get<Odds, string>();
            if (marketRange != null && marketRange.Any())
            {
                var query = from m in marketRange
                              join g in games on m.GameID equals g.GameID
                              select m;
                foreach (var mk in query)
                {
                    marketModels.AddOrUpdate(mk.Key, mk, (k, t) => mk);
                }
            }
            if (oddsRange != null && oddsRange.Any())
            {
                var query = from m in oddsRange
                              join g in games on m.GameID equals g.GameID
                              select m;
                foreach (var odds in query)
                {
                    oddsModels.AddOrUpdate(odds.Key, odds, (k, t) => odds);
                }
            }
            return games.Count;
        }

        public override int DeleteMany(params object[] keys)
        {
            if (keys == null)
                throw new ArgumentNullException("keys", "need keys");
            if (!keys.Any()) return 0;

            foreach (var key in keys)
            {
                Game del;
                if (BinModels.TryRemove(key.ToString(), out del))
                {
                    TryRemoveRelatedEntities(del);
                }
            }
            return keys.Length;
        }
    }
}
